﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using Fuse8.ContentManagementFramework.BizFacade.Services.Contents;
using Fuse8.ContentManagementFramework.BizFacade.Services.Contents.Queryable;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Web.ApplicationRuntime.ContentModuleModels;
using Fuse8.ContentManagementFramework.Web.Etc;
using Fuse8.DomainFramework.Common.Locator;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    public class CmsContentModuleController : Controller
    {
        public virtual ActionResult RenderContentModule(ContentModuleModel model)
        {
            var viewModel = new Dictionary<string, object>();

            foreach (var d in model.Sources)
            {
                var expression = GetExpression(d);

                IQueryable<GenericCmsEntity> list = expression == null
                                      ? GenericContentService.GetAll(d.ContentTypeId)
                                      : GenericContentService.GetAll(d.ContentTypeId).Where(expression);

                list = SortSource(list, d);
                list = JoinSource(list, d);

                list = SetPager(viewModel, d, list);

                InitViewModelForSource(viewModel, d.Name, list);
            }

            var resolver = DependencyServiceLocator.Current.GetInstance<ITemplateResolver>();
            string text = resolver.Resolve(model.Template, viewModel);
            return Content(text);
        }

        protected virtual IQueryable<GenericCmsEntity> JoinSource(IQueryable<GenericCmsEntity> list, ContentModuleDataSource dataSource)
        {
            if (dataSource.Joins != null)
            {
                foreach (var joinModel in dataSource.Joins)
                {
                    var selecModel = new string[0];
                    if (!string.IsNullOrEmpty(joinModel.Select))
                    {
                        selecModel = joinModel.Select.Split(',');
                    }
                    if (joinModel.TypeId.HasValue)
                    {
                        list = list.JoinContent(joinModel.TypeId.Value, joinModel.PrimaryKey, joinModel.ForeignKey,
                                                () => selecModel);
                    }
                    else if(!string.IsNullOrEmpty(joinModel.Type))
                    {
                        var type = Type.GetType(joinModel.Type, true, false);
                        list = list.JoinWith(joinModel.PrimaryKey, joinModel.ForeignKey, () => selecModel, type);
                    }
                }
            }
            return list;
        }

        protected virtual void InitViewModelForSource(Dictionary<string, object> viewModel, string name, IQueryable<GenericCmsEntity> list)
        {
            var items = list.ToList();
            viewModel[name] = items.Select(p => p.Values);
            viewModel[name + "Results"] = items.Count > 0;
            viewModel[name + "NoResults"] = items.Count == 0;

        }

        protected virtual IQueryable<GenericCmsEntity> SetPager(Dictionary<string, object> viewModel, ContentModuleDataSource d, IQueryable<GenericCmsEntity> list)
        {
            if (d.Pager != null && d.Pager.Enabled)
            {
                int page = Convert.ToInt32(Request.QueryString[d.Pager.PageKey] ?? "1");
                var total = GetCount(list);
                int pageSize = d.Pager.PageSize;
                list = list.Skip(d.Pager.PageSize*(page - 1)).Take(d.Pager.PageSize);
                var pm = new PagerViewModel
                             {
                                 AfterCurrentPages = new List<PagerItemModel>(),
                                 BeforeCurrentPages = new List<PagerItemModel>(),
                                 Key = d.Pager.PageKey,
                                 Url = Request.Url.AbsolutePath + "?"
                             };
                foreach (var key in Request.QueryString.AllKeys)
                {
                    if (key != pm.Key)
                    {
                        pm.Url += string.Format("{0}={1}&", key, Request.QueryString[key]);
                    }
                }
                pm.ShowPager = total > pageSize;
                var pageCount = (int) Math.Ceiling(total/(double) pageSize);
                int visibleLinksCount = d.Pager.VisibleLinkCount;
                int currentGroup = page%visibleLinksCount == 0
                                       ? ((int) Math.Floor((double) (page/visibleLinksCount))) - 1
                                       : (int) Math.Floor((decimal) (page/visibleLinksCount));

                int start = currentGroup*visibleLinksCount + 1;
                int end = currentGroup*visibleLinksCount + visibleLinksCount;

                if (start > visibleLinksCount)
                {
                    pm.PrevLink = new PagerItemModel() {Index = start - 1};
                }

                if (end < pageCount)
                {
                    pm.NextLink = new PagerItemModel() {Index = end + 1};
                }
                else
                {
                    end = pageCount;
                }

                for (int i = start; i <= end; i++)
                {
                    if (i < page)
                    {
                        pm.BeforeCurrentPages.Add(new PagerItemModel() {Index = i});
                    }
                    if (i == page)
                    {
                        pm.CurrentPage = new PagerItemModel() {Index = i};
                    }
                    if (i > page)
                    {
                        pm.AfterCurrentPages.Add(new PagerItemModel() {Index = i});
                    }
                }
                viewModel[d.Name + "Pager"] = pm;
            }
            return list;
        }

        protected virtual int GetCount(IQueryable<GenericCmsEntity> list)
        {
            return list.Count();
        }

        protected virtual IQueryable<GenericCmsEntity> SortSource(IQueryable<GenericCmsEntity> list, ContentModuleDataSource d)
        {
            var orderField = d.OrderField;
            if (!string.IsNullOrEmpty(orderField))
            {
                if (!d.Desc)
                {
                    list = list.OrderBy(p => p[orderField]);
                }
                else
                {
                    list = list.OrderByDescending(p => p[orderField]);
                }
            }
            return list;
        }

        protected virtual Expression<Func<GenericCmsEntity, bool>> GetExpression(ContentModuleDataSource d)
        {
            Expression<Func<GenericCmsEntity, bool>> expression = null;
            object value = null;
            var parameterName = d.Where.ParameterName;
            switch (d.Where.Type)
            {
                case WhereType.None:
                    break;
                case WhereType.Const:
                    value = d.Where.Value;
                    if (value != null)
                    {
                        expression = p => p.PropertyEquals(parameterName, value);
                    }
                    break;
                case WhereType.QueryString:
                    value = Request.QueryString[d.Where.Value];
                    if (value != null)
                    {
                        expression = p => p.PropertyEquals(parameterName, value);
                    }
                    break;
                case WhereType.Session:
                    value = Session[d.Where.Value];
                    if (value != null)
                    {
                        expression = p => p.PropertyEquals(parameterName, value);
                    }
                    break;
                case WhereType.MvcRouteData:
                    value = ControllerContext.RouteData.Values[d.Where.Value];
                    if (value != null)
                    {
                        expression = p => p.PropertyEquals(parameterName, value);
                    }
                    break;                
            }

            if (d.Where.Type != WhereType.None)
            {
                
                if (d.Where.Type == WhereType.QueryString && value != null)
                {
                    
                }
            }
            return expression;
        }
    }
}